package com.cfw.collection;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.tree.DefaultMutableTreeNode;

/**
 * Map만을 노드로 허용하는 Tree
 * 
 * history:
 * 	2013.9.26 - DefaultMutableTreeNode를 extends하여 구현하였으나, UserObject를 Map으로 제한할 방법이 없어,
 * 	inner class로 DefaultMutableTreeNode를 extends하여 구현하고, 필요한 method만 선언한 class로 변경함 
 * 	 
 * @author hipark
 *
 */
public class MapTree {
	private TreeHolder tree;
	
	/**
	 * DefaultMutableTreeNode를 확장
	 * holder에 대한 접근을 위해 Contructor 호출시 Holder역할을 수행하는 MapTree객체를 전달받아 member에 할당
	 * @author Administrator
	 */
	private class TreeHolder extends DefaultMutableTreeNode {
		private static final long serialVersionUID = -1987394878588138351L;
		private final MapTree holder;
		
		public TreeHolder(final MapTree mapTree) {
			super();
			holder = mapTree;
		}

		public TreeHolder(final MapTree mapTree, final Map<?, ?> map) {
			super(map);
			holder = mapTree;
		}

		public MapTree getHolder() {
			return holder;
		}
	}
	
	public MapTree(final Map map) {
		tree =	new TreeHolder(this, map);
	}
	
	public MapTree() {
		tree =	new TreeHolder(this);
	}

	/**
	 * TreeHolder 내부에 add method를 선언하고 이용하는 방법이 나은것으로 판단되나,
	 * MapTree의 instance 생성시 TreeHolder를 생성하게 되어 있어, 불가피하게 MapTree에서 처리함
	 * @param map
	 * @return
	 */
	public MapTree add(final Map map) {
		final MapTree child = new MapTree(map);
		
		tree.add(child.tree);
		
		return child;
	}
	
	public void setUserObject(Map<?, ?> map) {
		tree.setUserObject(map);	
	}
	
	public Object getUserObject() {
		return tree.getUserObject();
	}
	
	public Enumeration<?> depthFirstEnumeration() {
		return tree.depthFirstEnumeration();
	}
	
	public MapTree getNextSibling() {
		MapTree result = null;
		
		if (tree.getNextSibling() != null) { result = ((TreeHolder)tree.getNextSibling()).getHolder(); }
		
		return result;
	}
	
	public MapTree getFirstChild() {
		MapTree result = null;
		
		if (tree.getFirstChild() != null) { result = ((TreeHolder)tree.getFirstChild()).getHolder(); }	
		
		return result;
	}
	
	public boolean isLeaf() {
		return tree.isLeaf();
	}
	
	public Enumeration<MapTree> getChildren() {
		return tree.children();
	}
	
	/**
	 * 각 노드를 depth first search로 검색, 검색 중 callback.match 결과가 true인 첫번째 노드를 반환
	 * @param callback
	 * @return
	 */
	public MapTree search(final TreeSearch.AbstractMatchCallback callback) {
		final Enumeration<TreeHolder> allnode =	tree.depthFirstEnumeration();
		MapTree result = null;
		
		while (allnode.hasMoreElements()) {
			final MapTree eachnode = (MapTree)allnode.nextElement().getHolder();
			
			if (callback.match(eachnode)) {
				result = eachnode;
				break;
			}
		}
		return result;
	}
	
		/**
		 * Map의 clone은 존재하지 않음, HashMap을 사용해야 함
		 * @param list
		 * @param par
		 */
		@Deprecated
		private static void toList(final List<HashMap<?, ?>> list, final MapTree mapTree) {
			if (mapTree == null || list == null || mapTree.tree == null) {
				return;
			}
			
			for (int i = 0; i < mapTree.tree.getChildCount(); i++) {
				final MapTree.TreeHolder childTree = (MapTree.TreeHolder)mapTree.tree.getChildAt(i);
				final HashMap<Object, Object> childMap = (HashMap<Object, Object>)((HashMap<?, ?>)childTree.getUserObject()).clone();
				
				list.add(childMap);
				
				if (childTree.getChildCount() > 0) {
					final ArrayList<HashMap<?, ?>> arr = new ArrayList<HashMap<?, ?>>();
					childMap.put("_child", arr);
					toList(arr, childTree.holder);
				}
			}
		}
	
	/**
	 * private toList에서 clone을 위해 HashMap을 사용하여, 관련 파라메터는 모두 HashMap으로
	 * @param tree
	 * @return
	 */
	@Deprecated
	public static List<HashMap<?, ?>> toList(final MapTree mapTree) {
		final MapTree.TreeHolder root =	(MapTree.TreeHolder)mapTree.tree.getRoot();
		
		List<HashMap<?, ?>> arr =	null;
		
		if (root.getChildCount() > 0) {
			arr =	new ArrayList<HashMap<?, ?>>();
			toList(arr, root.holder);
		}
		
		return arr;
	}
	
	@Deprecated
	public List<HashMap<?, ?>> toListAll() {
		List<HashMap<?, ?>> arr = null;
		
		final MapTree.TreeHolder root =	(MapTree.TreeHolder)tree.getRoot();

		if (root != null && root.getChildCount() > 0) {
			arr = new ArrayList<HashMap<?, ?>>();
			toList(arr, root.holder);
		}
		
		return arr;
	}
}
